/*!
 *  \file       GaussRandomGenerator.cpp
 *  \brief      Implementazione della classe \c GaussRandomGenerator
 *  \author     Davide Scola <S2299453@studenti.unige.it>
 *  \author	Minetti Alberto <S3064777@studenti.unige.it>
 *  \version    1.0
 */
#include <cmath>
#include <utility>

#include <exception>
#include "InvalidArgumentException.hpp"

#include "IEntropyGenerator.hpp"
#include "IRandomGenerator.hpp"
#include "SimpleRandomGenerator.hpp"
#include "NormalizedRandomGenerator.hpp"
#include "IntervalRandomGenerator.hpp"
#include "GaussRandomGenerator.hpp"

using namespace std;


/*!
 *  \brief      Costruttore di copia di \c GaussRandomGenerator
 *  \param[in]	other	generatore da copiare
 */
template < typename tType >
GaussRandomGenerator< tType >::GaussRandomGenerator( GaussRandomGenerator< tType > const& other )
  : IntervalRandomGenerator< tType >( static_cast< IntervalRandomGenerator< tType > >( other ) ),
    mean_( (&other)->mean_ ),
    stdev_( (&other)->stdev_ )
{
  // minRange_ and maxRange_ must be -1 and 1 so don't copy 'em
}

/*!
 *  \brief      Costruttore di \c GaussRandomGenerator
 *  \param[in]	mean		media
 *  \param[in]	variance	varianza
 *  \param[in]	entropy		generatore di entropia da utilizzare
 *
 *  \exception	InvalidArgumentException	se viene passata una varianza negativa
 */
template < typename tType >
GaussRandomGenerator< tType >::GaussRandomGenerator( tType mean, tType variance, IEntropyGenerator & entropy )
  : IntervalRandomGenerator< tType >( tType( -1 ), tType( 1 ), entropy ),
    mean_( mean )
{
  if( variance < tType( 0 ) ) {
    throw InvalidArgumentException( );
  }
  
  this->stdev_ = tType( sqrt( variance ) );
}


/*!
 *  \brief      Operatore di assegnazione della classe \c GaussRandomGenerator
 *
 */
template < typename tType > GaussRandomGenerator< tType >&
GaussRandomGenerator< tType >::operator=( const GaussRandomGenerator& other )
{
   if (this == &other) {
       return *this;
   }
   
   // minRange_ and maxRange_ must be -1 and 1 so don't copy 'em
   this->entropy_ = (&other)->entropy_;
   this->mean_ = (&other)->mean_;
   this->stdev_ = (&other)->stdev_;
   
   return *this;
}


/*!
 *  \brief      Ritorna un nuovo numero pseudo-casuale con distribuzione gaussiana
 */
template < typename tType > tType
GaussRandomGenerator< tType >::next( void )
{
  tType w;
  pair< tType, tType > point;
  
  do {
    point = make_pair( IntervalRandomGenerator< tType >::next(),
		       IntervalRandomGenerator< tType >::next() );
    
    w = (&point)->first * (&point)->first + (&point)->second * (&point)->second;
  } while( w >= tType( 1 ) || w == tType( 0 ) );
  
  return this->mean_									+
	  (&point)->first * tType( sqrt( tType( -2 ) * tType( log( w ) ) / w ) )	*
	  this->stdev_;
}
